public class SelectListComponentController extends ComponentControllerBase{
    public list<Selectoption> ListOptions { 
    	get;
    	set{
    		setBaseOptions(value);
    	}
    }
    public integer Rows{get; set;} //Number of rows to show in the select lists
    public integer ListWidth{get; set;}
    
    public List<Selectoption> AvailableOptions { get; private set; }
    public List<Selectoption> ChosenOptions { get; private set; }
    public string[] SelectedAvailableOption { get; set; }
    public string[] SelectedChosenOption { get; set; }
    
	private map<string, integer> MasterPosition; //Where would the select option sit if it was held in the list supplied by the calling page.

    private void setBaseOptions(list<Selectoption> options){
    	//Because this gets called every time the page is reloaded,  ensure that the base data is only created once
    	
    	if(MasterPosition == null){
	        MasterPosition = new map<string, integer>();
	        AvailableOptions = new List<Selectoption>();
	        
	        integer i = 0;
	        for(Selectoption s : options){
	        	MasterPosition.put(s.getValue(), i++);
	        	AvailableOptions.add(s);
	        }
	        system.debug('***setBaseOptions.MasterPosition: ' + MasterPosition);
	        system.debug('***setBaseOptions.AvailableOptions: ' + AvailableOptions);
	        
	        ChosenOptions = new List<Selectoption>();
	        
	        SelectedAvailableOption = new string[]{};
	        SelectedChosenOption = new string[]{};
    	}
    }

    public Pagereference Add() {
        
        for(string selection : SelectedAvailableOption){  
        	system.debug('***Add.selection: ' + selection);
        	
        	//Ordinal for the item to be added
         	integer i = SelectedPostion(selection, AvailableOptions);
        	
        	ChosenOptions = InsertIntoList(AvailableOptions.get(i), ChosenOptions);
        	
        	AvailableOptions.remove(i);
        }
        system.debug('***Add.AvailableOptions: ' + AvailableOptions);
        system.debug('***Add.ChosenOptions: ' + ChosenOptions);

        return null;
    }
    
    public Pagereference Remove() {

        for(string selection : SelectedChosenOption){
        	system.debug('***Remove.selection: ' + selection);
        	
        	//Ordinal for the item to be removed
         	integer i = SelectedPostion(selection, ChosenOptions);
        	
        	AvailableOptions = InsertIntoList(ChosenOptions.get(i), AvailableOptions);
        	
        	ChosenOptions.remove(i);
        }
        system.debug('***Remove.AvailableOptions: ' + AvailableOptions);
        system.debug('***Remove.ChosenOptions: ' + ChosenOptions);

        return null;
    }
    
    public Pagereference ResetList(){
        system.debug('***ResetList.ChosenOptions: ' + ChosenOptions);
        
    	//Move everything from ChosenOptions to AvailableOptions
    	for(Selectoption so : ChosenOptions){
        	system.debug('***ResetList.so.getValue(): ' + so.getValue());
    		SelectedChosenOption.add(so.getValue());
    	}
    	
    	return Remove();
    }
    
    private integer SelectedPostion(string SelectedItem, list<Selectoption> OptionList){
    	system.debug('***SelectedPostion.OptionList: ' + OptionList);
    	
     	integer i = 0;
    	for(Selectoption option : OptionList){
    		system.debug('***SelectedPostion option.value:SelectedItem ' + option.getValue() + ':' + SelectedItem);
    		
    		if(option.getValue() == SelectedItem){
    			system.debug('***SelectedPostion MATCH (' + i + ')');
    			break;
    		}
    		i++;
    	}
    	system.debug('***SelectedPostion.return: ' + i);
    	
    	return i;
    }
    
    private list<Selectoption> InsertIntoList(Selectoption SelectedItem, list<Selectoption> ListToInsertInto){
    	integer insertPosition = MasterPosition.get(SelectedItem.getValue());
    	system.debug('***InsertIntoList.insertPosition: ' + insertPosition);
    	system.debug('***InsertIntoList.MasterPosition: ' + MasterPosition);
    	
    	list<selectoption> newSelectOptionList = new list<Selectoption>();
    	
    	if(ListToInsertInto.size() == 0){
    		system.debug('***InsertIntoList: Zero size list JUST ADD');
    		newSelectOptionList.add(SelectedItem);
    	}
    	else{
    		Selectoption firstItem = ListToInsertInto.get(0);
    		integer firstPosition = MasterPosition.get(firstItem.getValue());
    		system.debug('***InsertIntoList.firstPosition: ' + firstPosition);
    		
    		if(firstPosition > insertPosition){
    			//Add item to the beginning of the list and then add the rest
    			newSelectOptionList.add(SelectedItem);
    			newSelectOptionList.addAll(ListToInsertInto);
    		}
    		else{
	    		Selectoption lastItem = ListToInsertInto.get(ListToInsertInto.size() - 1);
	    		integer lastPosition = MasterPosition.get(lastItem.getValue());
	    		system.debug('***InsertIntoList.lastPosition: ' + lastPosition);
	    		
	    		if(lastPosition < insertPosition){
	    			//Easy,  just add the new item to the end of the list
	    			newSelectOptionList = ListToInsertInto.clone();
	    			newSelectOptionList.add(SelectedItem);
	    		}
	    		else{
	    			boolean itemHasNotBeenAdded = true;
	    			
	    			//The item to be added is somewhere in the middle
	    			for(integer i = 0 ; i < ListToInsertInto.size() ; i++){
	    				Selectoption currentItem = ListToInsertInto.get(i);
	    				integer currentPosition = MasterPosition.get(currentItem.getValue());
	    				system.debug('***InsertIntoList.currentPosition: ' + currentPosition);
	    		
	    				if(currentPosition > insertPosition && itemHasNotBeenAdded){
	    					newSelectOptionList.add(SelectedItem);
	    					newSelectOptionList.add(ListToInsertInto.get(i));
	    					
	    					itemHasNotBeenAdded = false;
	    				}
	    				else{
	    					newSelectOptionList.add(ListToInsertInto.get(i));
	    				}
	    			}
    			}
    		}
    	}
    	
    	return newSelectOptionList;
    }
}